package jacky.lanlan.song.test.common;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.util.*;

/**
 * 随机数据生成器。
 * <p>
 * 提供 {@code Integer}、{@code Double}、{@code Date}、{@code Boolean}、{@code String}、{@code List}、
 * {@code Set}、{@code Map}、{@code Array} 这些类型的随机数据。
 * <p>
 * 对于集合类型，每个集合生成2个元素。<br>
 * 如果集合元素是自身(循环引用，类似Composite模式那种)，则向下递归一层，生成2个同类型元素。
 * 
 * @author Jacky.Song
 */
public enum RandomDataGenerator {
	Integer {
		@Override
		public boolean matchType(Class<?> type) {
			return INTEGER_TYPE.contains(type);
		}
		@Override
		public Object randomValue(Class<?>... types) {
			byte value = (byte) new Random().nextInt(255);// 最多3位
			try {
				return types[0].isPrimitive() ? value : types[0].getMethod("valueOf", String.class)
						.invoke(types[0], value + "");
			}
			catch (Exception e) {
				return null;
			}
		}
	},
	
	Double {
		@Override
		public boolean matchType(Class<?> type) {
			return FLOAT_TYPE.contains(type);
		}
		
		@Override
		public Object randomValue(Class<?>... types) {
			float value = (float) Math.random();
			try {
				return types[0].isPrimitive() ? value : types[0].getMethod("valueOf", String.class)
						.invoke(types[0], value + "");
			}
			catch (Exception e) {
				return null;
			}
		}
	},
	
	Date {
		@Override
		public boolean matchType(Class<?> type) {
			return type == java.util.Date.class || type == java.sql.Date.class;
		}
		
		@Override
		public Object randomValue(Class<?>... types) {
			long random = (long) (Math.random() * System.currentTimeMillis());
			return types[0] == java.util.Date.class ? new java.util.Date(random) : new java.sql.Date(random);
		}
	},
	
	Boolean {
		@Override
		public boolean matchType(Class<?> type) {
			return type == java.lang.Boolean.class || type == boolean.class;
		}
		
		@Override
		public Object randomValue(Class<?>... types) {
			boolean value = ((Byte) Integer.randomValue(types) % 2 == 0);
			return types[0].isPrimitive() ? value : java.lang.Boolean.valueOf(value);
		}
	},
	
	Char {
		@Override
		public boolean matchType(Class<?> type) {
			return type == char.class;
		}
		
		@Override
		public Object randomValue(Class<?>... types) {
			return (char) ((Byte) Integer.randomValue(types)).intValue();
		}
	},
	
	String {
		@Override
		public boolean matchType(Class<?> type) {
			return type == java.lang.String.class;
		}
		
		@Override
		public Object randomValue(Class<?>... types) {
			Random r = new Random();
			int maxLength = r.nextInt(11) + 4;// 4-15位
			StringBuilder sb = new StringBuilder();

			// 如果是空字符串，随机直到不为空为止
			do {
				this.randomString(maxLength / 2, sb, r);
				this.randomChineseString(maxLength / 2, sb, r);
			}
			while (sb.toString().trim().length() == 0);
			
			return sb.toString();
		}
		
		private void randomString(int length, StringBuilder sb, Random r) {
			for (int i = 0; i < length; i++) {
				sb.append((char) (r.nextInt(48) + 122 - 48));// ASCII 48-122，包涵了大部分可见字符
			}
		}
		
		private void randomChineseString(int length, StringBuilder sb, Random r) {
			try {
				for (int i = 0; i < length; i++) {
					byte[] bt = new byte[2];
					bt[0] = (byte) ((byte) 0xA1 + r.nextInt(71) + 16);// 区码 16-87
					bt[1] = (byte) ((byte) 0xA1 + r.nextInt(93) + 1);// 位码 1-94
					
					sb.append(new String(bt, "gb2312"));
				}
			}
			catch (Exception e) {}
		}
		
	},
	
	List {
		@Override
		public boolean matchType(Class<?> type) {
			return java.util.List.class.isAssignableFrom(type);
		}
		
		@Override
		public Object randomValue(Class<?>... types) {
			List list = null;
			try {
				list = types[0].isInterface() ? new ArrayList(2) : (List) types[0].newInstance();
				Class<?> elementType = types[1];
				for (int i = 0; i < 2; i++) {// 2个
					// 下面比较元素是否是递归引用，避免无限递归，下面Set/Map同理
					list.add(CICURAL_REF.get().contains(elementType) ? elementType.newInstance()
							: geneRandomValue(elementType));
				}
			}
			catch (Exception e) {
				e.printStackTrace();
			}
			return list;
		}
	},
	
	Set {
		@Override
		public boolean matchType(Class<?> type) {
			return java.util.Set.class.isAssignableFrom(type);
		}
		
		@Override
		public Object randomValue(Class<?>... types) {
			Set set = null;
			try {
				set = types[0].isInterface() ? new HashSet(2) : (Set) types[0].newInstance();
				Class<?> elementType = types[1];
				for (int i = 0; i < 2; i++) {// 2个
					set
							.add(CICURAL_REF.get().contains(elementType) ? elementType.newInstance()
									: geneRandomValue(elementType));
				}
			}
			catch (Exception e) {

			}
			return set;
		}
	},
	
	Map {
		@Override
		public boolean matchType(Class<?> type) {
			return java.util.Map.class.isAssignableFrom(type);
		}
		
		@Override
		public Object randomValue(Class<?>... types) {
			Map map = null;
			try {
				map = types[0].isInterface() ? new HashMap(3, 1) : (Map) types[0].newInstance();
				Object key = null;
				Object value = null;
				Class<?> elementType = types[1];
				Class<?> valueType = types[2];
				for (int i = 0; i < 2; i++) {// 2个
					key = CICURAL_REF.get().contains(elementType) ? elementType.newInstance() : geneRandomValue(elementType);
					value = CICURAL_REF.get().contains(valueType) ? valueType.newInstance() : geneRandomValue(valueType);
					map.put(key, value);
				}
			}
			catch (Exception e) {
				
			}
			return map;
		}
	},
	
	Array {

		
		@Override
		public boolean matchType(Class<?> type) {
			return type.isArray();
		}
		
		@Override
		public Object randomValue(Class<?>... types) {
			int length = (int) (Math.random() * 7 + 3);// 3 - 10
			Class<?> elementType = types[0].getComponentType();

			Object arr = java.lang.reflect.Array.newInstance(elementType, length);
			for (int i = 0; i < length; i++) {
				java.lang.reflect.Array.set(arr, i, RandomDataGenerator.geneRandomValue(elementType));
			}
			
			return arr;
		}
		
	},
	
	Others {

		@Override
		public boolean matchType(Class<?> type) {

			try {
				// 抽象类无法反射new，直接无视
				if (Modifier.isAbstract(type.getModifiers())) return false;
			}
			catch (Exception e) {}

			for (RandomDataGenerator gene : values()) {
				if (gene != this && gene.matchType(type)) return false;
			}
			return true;
		}
		
		@Override
		public Object randomValue(Class<?>... types) {
			Object obj = null;
			CICURAL_REF.get().add(types[0]);
			try {
				obj = types[0].newInstance();
				
				for (Field field : types[0].getDeclaredFields()) {
					
					Class<?> fieldType = field.getType();
					Object value = null;
					Class<?> elementType = null;
					Class<?> valueType = null;
					if (RandomDataGenerator.List.matchType(fieldType) || RandomDataGenerator.Set.matchType(fieldType)) {
						elementType = getTypeOfCollection(field);
					}
					else if (RandomDataGenerator.Map.matchType(fieldType)) {
						Class<?>[] arr = getKVTypeOfMap(field);
						elementType = arr[0];
						valueType = arr[1];
					}
					
					if (!CICURAL_REF.get().contains(fieldType)) // 不存在循环引用
						value = geneRandomValue(fieldType, elementType, valueType);
					
					field.setAccessible(true);
					field.set(obj, value);
				}
			}
			catch (Exception e) {
				e.printStackTrace();
			}
			
			CICURAL_REF.get().remove(types[0]);
			return obj;
		}
		
	},
	
	;
	
	/**
	 * 生成填入随机值的目标类型对象。
	 * 
	 * @param types
	 *          类型列表
	 *          <ul>
	 *          <li>[0]-目标类型</li> <li>
	 *          [1]-如果是type是集合类型，则这个指定元素类型，如果type是Map类型，则这个指定Key类型</li> <li>
	 *          [2]-如果type是Map类型，则这个参数进一步指定Value类型</li>
	 *          </ul>
	 * @return
	 */
	public static Object geneRandomValue(Class<?>... types) {
		Class<?> elementType = types.length >= 2 ? types[1] : null;
		Class<?> valueType = types.length >= 3 ? types[2] : null;
		for (RandomDataGenerator gene : values()) {
			if (gene.matchType(types[0])) return gene.randomValue(types[0], elementType, valueType);
		}
		return null;
	}
	
	public abstract boolean matchType(Class<?> type);
	
	public abstract Object randomValue(Class<?>... types);
	
	// 查泛型集合的元素类型
	private static Class<?> getTypeOfCollection(Field field) {
		ParameterizedType pType = (ParameterizedType) field.getGenericType();
		return (Class<?>) pType.getActualTypeArguments()[0];
	}
	
	// 查泛型Map的 Key,Value 类型，[0] = Key类型，[1] = Value类型
	private static Class<?>[] getKVTypeOfMap(Field field) {
		ParameterizedType pType = (ParameterizedType) field.getGenericType();
		return new Class<?>[] { (Class<?>) pType.getActualTypeArguments()[0], (Class<?>) pType.getActualTypeArguments()[1] };
	}

	// 每次要转换的对象类型，避免循环引用导致无限递归
	private static final ThreadLocal<Set<Class<?>>> CICURAL_REF = new ThreadLocal<Set<Class<?>>>() {
		@Override
		protected java.util.Set<Class<?>> initialValue() {
			return new HashSet<Class<?>>();
		}
	};
	
	private static final Set<Class<?>> FLOAT_TYPE = new HashSet<Class<?>>(4);
	
	private static final Set<Class<?>> INTEGER_TYPE = new HashSet<Class<?>>(8);
	
	static {
		FLOAT_TYPE.addAll(Arrays.asList(new Class<?>[] { double.class, Double.class, float.class, Float.class }));
		
		INTEGER_TYPE.addAll(Arrays.asList(new Class<?>[] { int.class, Integer.class, byte.class, Byte.class,
				short.class,Short.class, long.class, Long.class }));
	}
	
}
